package com.neuronrobotics.replicator.gui.preview.controller;

import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;

import com.neuronrobotics.replicator.gui.preview.view.STLWorkspaceView;


public class DefaultGeneralCameraController implements GeneralCameraController {
	
	private STLWorkspaceView theView;
	
	private float[] cameraPosition = new float[3], cameraFocus = new float[3];
	
	public DefaultGeneralCameraController(STLWorkspaceView view){
		theView = view;
	}
		
	@Override
	public void zoom(float dist) {
		this.validateCameraData();

		Point3f tempPos, tempDir;

		tempDir = new Point3f(cameraFocus);
		tempPos = new Point3f(cameraPosition);

		Vector3f dirVector = new Vector3f();
		dirVector.add(tempDir);
		dirVector.sub(tempPos);
		dirVector.normalize();
		dirVector.scale(dist);
		// System.out.println(dirVector);

		// Prevents zooming past focal point
		if (dist <= tempPos.distance(tempDir))
			tempPos.add(dirVector);
		
		tempPos.get(cameraPosition);
		tempDir.get(cameraFocus);

		theView.setCamera(cameraPosition,cameraFocus);
	}

	@Override
	public void panX(float dist) {
		validateCameraData();

		Point3f tempPos, tempDir;

		tempDir = new Point3f(cameraFocus);
		tempPos = new Point3f(cameraPosition);

		tempPos.add(new Point3f(0, 0, dist));
		tempDir.add(new Point3f(0, 0, dist));
		tempPos.get(cameraPosition);
		tempDir.get(cameraFocus);

		theView.setCamera(cameraPosition,cameraFocus);
	}

	@Override
	public void panY(float dist) {
		validateCameraData();
		
		Point3f tempPos, tempDir;

		tempDir = new Point3f(cameraFocus);
		tempPos = new Point3f(cameraPosition);

		tempPos.add(new Point3f(0, dist, 0));
		tempDir.add(new Point3f(0, dist, 0));
		tempPos.get(cameraPosition);
		tempDir.get(cameraFocus);

		theView.setCamera(cameraPosition,cameraFocus);
	}

	@Override
	public void panZ(float dist) {
		validateCameraData();
		
		Point3f tempPos, tempDir;

		tempDir = new Point3f(cameraFocus);
		tempPos = new Point3f(cameraPosition);

		tempPos.add(new Point3f(dist , 0, 0));
		tempDir.add(new Point3f(dist , 0, 0));
		tempPos.get(cameraPosition);
		tempDir.get(cameraFocus);

		theView.setCamera(cameraPosition,cameraFocus);
	}

	@Override
	public void panTo(float[] focus) {
		validateCameraData();
		
		Point3f tempPos, tempDir;
		
		tempDir = new Point3f(cameraFocus);
		tempPos = new Point3f(cameraPosition);

		Vector3f newPos = new Vector3f(0, 0, 0);
		newPos.add(tempPos);
		newPos.sub(tempDir);
		newPos.add(new Point3f(focus));

		tempDir = new Point3f(focus);
		tempPos = new Point3f(newPos);

		tempPos.get(cameraPosition);
		tempDir.get(cameraFocus);

		theView.setCamera(cameraPosition,cameraFocus);
	}
	
	@Override
	public void revolveCameraXZ(float radians) {
		validateCameraData();
		
		Point3f tempPos, tempDir;

		tempDir = new Point3f(cameraFocus);
		tempPos = new Point3f(cameraPosition);

		Vector3f vec = new Vector3f();
		vec.add(tempPos);
		vec.sub(tempDir);

		float radius = (float)Math.sqrt((vec.x * vec.x) + (vec.z * vec.z));

		float currentAngle = (float)Math.acos(vec.x / radius);
		if (vec.z < 0)
			currentAngle = (float)(2 * Math.PI) - currentAngle;

		float newAngle = currentAngle +radians;
		float newX = (float)(radius * Math.cos(newAngle) + tempDir.x);
		float newZ = (float)(radius * Math.sin(newAngle) + tempDir.z);

		/*
		 * System.out.println("Radius: "+radius);
		 * System.out.println("Initial Angle: "+currentAngle);
		 * System.out.println("Delta Angle: "+radians);
		 * System.out.println("Final Angle:" + newAngle);
		 * System.out.println("Initial position: "+tempPos);
		 */
		tempPos = new Point3f(newX, tempPos.y, newZ);
		/*
		 * System.out.println("Final position: "+tempPos);
		 * System.out.println("//////////////");
		 */

		tempPos.get(cameraPosition);
		tempDir.get(cameraFocus);

		theView.setCamera(cameraPosition,cameraFocus);
	}

	@Override
	public void revolveCameraUp(float radians) {
		validateCameraData();
		
		Point3f tempPos, tempDir;

		tempDir = new Point3f(cameraFocus);
		tempPos = new Point3f(cameraPosition);

		Vector3f positionVector = new Vector3f();
		positionVector.add(tempPos);
		positionVector.sub(tempDir);

		float radius = positionVector.length();

		float currentAngle = (float)Math.asin(positionVector.y / radius);

		float newAngle = radians + currentAngle;

		float newY =(float)( Math.sin(newAngle) * radius);

		float t = positionVector.x / positionVector.z;

		float newZ = (float)(Math.sqrt(((radius * radius) - (newY * newY))
				/ ((t * t) + 1)));

		if (positionVector.z < 0)
			newZ *= -1;

		float newX = newZ * t;

		newY += tempDir.y;
		newZ += tempDir.z;
		newX += tempDir.x;

		tempPos = new Point3f(newX, newY, newZ);

		tempPos.get(cameraPosition);
		tempDir.get(cameraFocus);

		theView.setCamera(cameraPosition,cameraFocus);
	}

	@Override
	public void setCamera(float[] position, float[] focus) {
		theView.setCamera(position, focus);		
		validateCameraData();
	}
	
	private void validateCameraData(){
		theView.getCameraData(cameraPosition, cameraFocus);
	}

}
